package com.mate.cloud.redisnetworkmodel.NIO;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * NIO 实现简单的 TCP 服务器
 *
 * @author: MI
 * @email: 448341911@qq.com
 * @createTime: 2025/9/3 14:19
 * @updateUser: MI
 * @updateTime: 2025/9/3 14:19
 * @updateRemark: 修改内容
 * @version: 1.0
 */
public class NioTcpServer {
    public static void main(String[] args) throws IOException {
        // 1. 创建 Selector（选择器，核心组件）
        Selector selector = Selector.open();

        // 2. 创建 ServerSocketChannel（服务端通道），并配置为非阻塞
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false); // 关键：设置为非阻塞模式
        serverChannel.bind(new InetSocketAddress(8080)); // 绑定 8080 端口

        // 3. 将服务端通道注册到 Selector，监听“连接就绪”事件（OP_ACCEPT）
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("NIO 服务器启动，监听 8080 端口...");

        // 4. 线程循环处理就绪事件（一个线程处理所有连接）
        while (true) {
            // 4.1 等待就绪事件（select()：无就绪事件时阻塞，有则返回就绪事件数；也可传超时时间避免永久阻塞）
            int readyChannels = selector.select();
            if (readyChannels == 0) continue; // 没有就绪事件，继续循环

            // 4.2 获取所有就绪事件的 SelectionKey（每个 Key 对应一个注册的通道）
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();

            // 4.3 遍历处理每个就绪事件
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();

                // 4.3.1 事件类型 1：连接就绪（客户端发起连接）
                if (key.isAcceptable()) {
                    // 获取服务端通道，接受客户端连接
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel clientChannel = server.accept(); // 非阻塞，立即返回（即使没有连接也不阻塞）
                    if (clientChannel != null) { // 确保有客户端连接
                        clientChannel.configureBlocking(false); // 客户端通道也必须设为非阻塞
                        System.out.println("新客户端连接：" + clientChannel.getRemoteAddress());

                        // 将客户端通道注册到 Selector，监听“读就绪”事件（OP_READ），并绑定一个缓冲区（用于后续读数据）
                        clientChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    }
                }

                // 4.3.2 事件类型 2：读就绪（客户端发送数据）
                else if (key.isReadable()) {
                    // 获取客户端通道和绑定的缓冲区
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = (ByteBuffer) key.attachment();

                    // 非阻塞读取数据（read()：没有数据时返回 -1，不会阻塞线程）
                    int len = clientChannel.read(buffer);
                    if (len > 0) {
                        // 切换缓冲区为“读模式”（flip()：将指针从写位置移到读位置）
                        buffer.flip();
                        String clientMsg = new String(buffer.array(), 0, buffer.limit());
                        System.out.println("收到客户端消息：" + clientMsg);

                        // 向客户端返回响应（简单处理：直接返回收到的消息）
                        String response = "服务器已收到：" + clientMsg;
                        ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
                        clientChannel.write(responseBuffer); // 非阻塞写（即使没写完也不阻塞）

                        // 清空缓冲区，准备下次读（clear()：重置指针，覆盖旧数据）
                        buffer.clear();
                    } else if (len == -1) {
                        // 客户端关闭连接，取消 SelectionKey 并关闭通道
                        key.cancel();
                        clientChannel.close();
                        System.out.println("客户端连接关闭：" + clientChannel.getRemoteAddress());
                    }
                }

                // 4.3.3 移除已处理的 SelectionKey（避免重复处理）
                keyIterator.remove();
            }
        }
    }

}
